﻿#region Header

// <file>
//     <copyright see="prj:///doc/copyright.txt"/>
//     <license see="prj:///doc/license.txt"/>
//     <owner name="Mike Krüger" email="mike@icsharpcode.net"/>
//     <version>$Revision: 3036 $</version>
// </file>

#endregion Header

namespace ICSharpCode.TextEditor.Document
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Text;

    public class DefaultHighlightingStrategy : IHighlightingStrategyUsingRuleSets
    {
        #region Fields

        protected HighlightRuleSet activeRuleSet;
        protected Span activeSpan;
        protected int currentLength;

        // Line state variable
        protected LineSegment currentLine;
        protected int currentLineNumber;

        // Line scanning state variables
        protected int currentOffset;

        // Span stack state variable
        protected SpanStack currentSpanStack;

        // Span state variables
        protected bool inSpan;

        HighlightRuleSet defaultRuleSet = null;

        //        internal void SetDefaultColor(HighlightBackground color)
        //        {
        //            return (HighlightColor)environmentColors[name];
        //            defaultColor = color;
        //        }
        HighlightColor defaultTextColor;
        HighlightColor digitColor;
        Dictionary<string, HighlightColor> environmentColors = new Dictionary<string, HighlightColor>();
        string[] extensions;
        string name;
        Dictionary<string, string> properties = new Dictionary<string, string>();
        List<HighlightRuleSet> rules = new List<HighlightRuleSet>();

        #endregion Fields

        #region Constructors

        public DefaultHighlightingStrategy()
            : this("Default")
        {
        }

        public DefaultHighlightingStrategy(string name)
        {
            this.name = name;

            digitColor       = new HighlightColor(SystemColors.WindowText, false, false);
            defaultTextColor = new HighlightColor(SystemColors.WindowText, false, false);

            // set small 'default color environment'
            environmentColors["Default"]          = new HighlightBackground("WindowText", "Window", false, false);
            environmentColors["Selection"]        = new HighlightColor("HighlightText", "Highlight", false, false);
            environmentColors["VRuler"]           = new HighlightColor("ControlLight", "Window", false, false);
            environmentColors["InvalidLines"]     = new HighlightColor(Color.Red, false, false);
            environmentColors["CaretMarker"]      = new HighlightColor(Color.LightCyan,false,false);
            environmentColors["LineNumbers"]      = new HighlightBackground("ControlDark", "Window", false, false);

            environmentColors["FoldLine"]         = new HighlightColor(Color.FromArgb(0x80, 0x80, 0x80), Color.Black, false, false);
            environmentColors["FoldMarker"]       = new HighlightColor(Color.FromArgb(0x80, 0x80, 0x80), Color.White, false, false);
            environmentColors["SelectedFoldLine"] = new HighlightColor(Color.Black, false, false);
            environmentColors["EOLMarkers"]       = new HighlightColor("ControlLight", "Window", false, false);
            environmentColors["SpaceMarkers"]     = new HighlightColor("ControlLight", "Window", false, false);
            environmentColors["TabMarkers"]       = new HighlightColor("ControlLight", "Window", false, false);
        }

        #endregion Constructors

        #region Properties

        public HighlightColor DefaultTextColor
        {
            get {
                return defaultTextColor;
            }
        }

        public HighlightColor DigitColor
        {
            get {
                return digitColor;
            }
            set {
                digitColor = value;
            }
        }

        public IEnumerable<KeyValuePair<string, HighlightColor>> EnvironmentColors
        {
            get {
                return environmentColors;
            }
        }

        public string[] Extensions
        {
            set {
                extensions = value;
            }
            get {
                return extensions;
            }
        }

        public string Name
        {
            get {
                return name;
            }
        }

        public Dictionary<string, string> Properties
        {
            get {
                return properties;
            }
        }

        public List<HighlightRuleSet> Rules
        {
            get {
                return rules;
            }
        }

        #endregion Properties

        #region Methods

        public void AddRuleSet(HighlightRuleSet aRuleSet)
        {
            HighlightRuleSet existing = FindHighlightRuleSet(aRuleSet.Name);
            if (existing != null) {
                existing.MergeFrom(aRuleSet);
            } else {
                rules.Add(aRuleSet);
            }
        }

        public HighlightRuleSet FindHighlightRuleSet(string name)
        {
            foreach(HighlightRuleSet ruleSet in rules) {
                if (ruleSet.Name == name) {
                    return ruleSet;
                }
            }
            return null;
        }

        public HighlightColor GetColor(IDocument document, LineSegment currentSegment, int currentOffset, int currentLength)
        {
            return GetColor(defaultRuleSet, document, currentSegment, currentOffset, currentLength);
        }

        public HighlightColor GetColorFor(string name)
        {
            HighlightColor color;
            if (environmentColors.TryGetValue(name, out color))
                return color;
            else
                return defaultTextColor;
        }

        public HighlightRuleSet GetRuleSet(Span aSpan)
        {
            if (aSpan == null) {
                return this.defaultRuleSet;
            } else {
                if (aSpan.RuleSet != null)
                {
                    if (aSpan.RuleSet.Reference != null) {
                        return aSpan.RuleSet.Highlighter.GetRuleSet(null);
                    } else {
                        return aSpan.RuleSet;
                    }
                } else {
                    return null;
                }
            }
        }

        public virtual void MarkTokens(IDocument document)
        {
            if (Rules.Count == 0) {
                return;
            }

            int lineNumber = 0;

            while (lineNumber < document.TotalNumberOfLines) {
                LineSegment previousLine = (lineNumber > 0 ? document.GetLineSegment(lineNumber - 1) : null);
                if (lineNumber >= document.LineSegmentCollection.Count) { // may be, if the last line ends with a delimiter
                    break;                                                // then the last line is not in the collection :)
                }

                currentSpanStack = ((previousLine != null && previousLine.HighlightSpanStack != null) ? previousLine.HighlightSpanStack.Clone() : null);

                if (currentSpanStack != null) {
                    while (!currentSpanStack.IsEmpty && currentSpanStack.Peek().StopEOL)
                    {
                        currentSpanStack.Pop();
                    }
                    if (currentSpanStack.IsEmpty) currentSpanStack = null;
                }

                currentLine = (LineSegment)document.LineSegmentCollection[lineNumber];

                if (currentLine.Length == -1) { // happens when buffer is empty !
                    return;
                }

                currentLineNumber = lineNumber;
                List<TextWord> words = ParseLine(document);
                // Alex: clear old words
                if (currentLine.Words != null) {
                    currentLine.Words.Clear();
                }
                currentLine.Words = words;
                currentLine.HighlightSpanStack = (currentSpanStack==null || currentSpanStack.IsEmpty) ? null : currentSpanStack;

                ++lineNumber;
            }
            document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.WholeTextArea));
            document.CommitUpdate();
            currentLine = null;
        }

        public virtual void MarkTokens(IDocument document, List<LineSegment> inputLines)
        {
            if (Rules.Count == 0) {
                return;
            }

            Dictionary<LineSegment, bool> processedLines = new Dictionary<LineSegment, bool>();

            bool spanChanged = false;
            int documentLineSegmentCount = document.LineSegmentCollection.Count;

            foreach (LineSegment lineToProcess in inputLines) {
                if (!processedLines.ContainsKey(lineToProcess)) {
                    int lineNumber = lineToProcess.LineNumber;
                    bool processNextLine = true;

                    if (lineNumber != -1) {
                        while (processNextLine && lineNumber < documentLineSegmentCount) {
                            processNextLine = MarkTokensInLine(document, lineNumber, ref spanChanged);
                            processedLines[currentLine] = true;
                            ++lineNumber;
                        }
                    }
                }
            }

            if (spanChanged || inputLines.Count > 20) {
                // if the span was changed (more than inputLines lines had to be reevaluated)
                // or if there are many lines in inputLines, it's faster to update the whole
                // text area instead of many small segments
                document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.WholeTextArea));
            } else {
            //				document.Caret.ValidateCaretPos();
            //				document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.SingleLine, document.GetLineNumberForOffset(document.Caret.Offset)));
            //
                foreach (LineSegment lineToProcess in inputLines) {
                    document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.SingleLine, lineToProcess.LineNumber));
                }

            }
            document.CommitUpdate();
            currentLine = null;
        }

        public void ResolveReferences()
        {
            // Resolve references from Span definitions to RuleSets
            ResolveRuleSetReferences();
            // Resolve references from RuleSet defintitions to Highlighters defined in an external mode file
            ResolveExternalReferences();
        }

        public void SetColorFor(string name, HighlightColor color)
        {
            if (name == "Default")
                defaultTextColor = new HighlightColor(color.Color, color.Bold, color.Italic);
            environmentColors[name] = color;
        }

        protected virtual HighlightColor GetColor(HighlightRuleSet ruleSet, IDocument document, LineSegment currentSegment, int currentOffset, int currentLength)
        {
            if (ruleSet != null) {
                if (ruleSet.Reference != null) {
                    return ruleSet.Highlighter.GetColor(document, currentSegment, currentOffset, currentLength);
                } else {
                    return (HighlightColor)ruleSet.KeyWords[document,  currentSegment, currentOffset, currentLength];
                }
            }
            return null;
        }

        protected void ImportSettingsFrom(DefaultHighlightingStrategy source)
        {
            if (source == null)
                throw new ArgumentNullException("source");
            properties = source.properties;
            extensions = source.extensions;
            digitColor = source.digitColor;
            defaultRuleSet = source.defaultRuleSet;
            name = source.name;
            rules = source.rules;
            environmentColors = source.environmentColors;
            defaultTextColor = source.defaultTextColor;
        }

        protected virtual void OnParsedLine(IDocument document, LineSegment currentLine, List<TextWord> words)
        {
        }

        protected virtual bool OverrideSpan(string spanBegin, IDocument document, List<TextWord> words, Span span, ref int lineOffset)
        {
            return false;
        }

        /// <summary>
        /// get the string, which matches the regular expression expr,
        /// in string s2 at index
        /// </summary>
        static string GetRegString(LineSegment lineSegment, char[] expr, int index, IDocument document)
        {
            int j = 0;
            StringBuilder regexpr = new StringBuilder();

            for (int i = 0; i < expr.Length; ++i, ++j) {
                if (index + j >= lineSegment.Length)
                    break;

                switch (expr[i]) {
                    case '@': // "special" meaning
                        ++i;
                        switch (expr[i]) {
                            case '!': // don't match the following expression
                                StringBuilder whatmatch = new StringBuilder();
                                ++i;
                                while (i < expr.Length && expr[i] != '@') {
                                    whatmatch.Append(expr[i++]);
                                }
                                break;
                            case '@': // matches @
                                regexpr.Append(document.GetCharAt(lineSegment.Offset + index + j));
                                break;
                        }
                        break;
                    default:
                        if (expr[i] != document.GetCharAt(lineSegment.Offset + index + j)) {
                            return regexpr.ToString();
                        }
                        regexpr.Append(document.GetCharAt(lineSegment.Offset + index + j));
                        break;
                }
            }
            return regexpr.ToString();
        }

        /// <summary>
        /// returns true, if the get the string s2 at index matches the expression expr
        /// </summary>
        static bool MatchExpr(LineSegment lineSegment, char[] expr, int index, IDocument document, bool ignoreCase)
        {
            for (int i = 0, j = 0; i < expr.Length; ++i, ++j) {
                switch (expr[i]) {
                    case '@': // "special" meaning
                        ++i;
                        if (i < expr.Length) {
                            switch (expr[i]) {
                                case 'C': // match whitespace or punctuation
                                    if (index + j == lineSegment.Offset || index + j >= lineSegment.Offset + lineSegment.Length) {
                                        // nothing (EOL or SOL)
                                    } else {
                                        char ch = document.GetCharAt(lineSegment.Offset + index + j);
                                        if (!Char.IsWhiteSpace(ch) && !Char.IsPunctuation(ch)) {
                                            return false;
                                        }
                                    }
                                    break;
                                case '!': // don't match the following expression
                                    {
                                        StringBuilder whatmatch = new StringBuilder();
                                        ++i;
                                        while (i < expr.Length && expr[i] != '@') {
                                            whatmatch.Append(expr[i++]);
                                        }
                                        if (lineSegment.Offset + index + j + whatmatch.Length < document.TextLength) {
                                            int k = 0;
                                            for (; k < whatmatch.Length; ++k) {
                                                char docChar = ignoreCase ? Char.ToUpperInvariant(document.GetCharAt(lineSegment.Offset + index + j + k)) : document.GetCharAt(lineSegment.Offset + index + j + k);
                                                char spanChar = ignoreCase ? Char.ToUpperInvariant(whatmatch[k]) : whatmatch[k];
                                                if (docChar != spanChar) {
                                                    break;
                                                }
                                            }
                                            if (k >= whatmatch.Length) {
                                                return false;
                                            }
                                        }
            //									--j;
                                        break;
                                    }
                                case '-': // don't match the  expression before
                                    {
                                        StringBuilder whatmatch = new StringBuilder();
                                        ++i;
                                        while (i < expr.Length && expr[i] != '@') {
                                            whatmatch.Append(expr[i++]);
                                        }
                                        if (index - whatmatch.Length >= 0) {
                                            int k = 0;
                                            for (; k < whatmatch.Length; ++k) {
                                                char docChar = ignoreCase ? Char.ToUpperInvariant(document.GetCharAt(lineSegment.Offset + index - whatmatch.Length + k)) : document.GetCharAt(lineSegment.Offset + index - whatmatch.Length + k);
                                                char spanChar = ignoreCase ? Char.ToUpperInvariant(whatmatch[k]) : whatmatch[k];
                                                if (docChar != spanChar)
                                                    break;
                                            }
                                            if (k >= whatmatch.Length) {
                                                return false;
                                            }
                                        }
            //									--j;
                                        break;
                                    }
                                case '@': // matches @
                                    if (index + j >= lineSegment.Length || '@' != document.GetCharAt(lineSegment.Offset + index + j)) {
                                        return false;
                                    }
                                    break;
                            }
                        }
                        break;
                    default:
                        {
                            if (index + j >= lineSegment.Length) {
                                return false;
                            }
                            char docChar = ignoreCase ? Char.ToUpperInvariant(document.GetCharAt(lineSegment.Offset + index + j)) : document.GetCharAt(lineSegment.Offset + index + j);
                            char spanChar = ignoreCase ? Char.ToUpperInvariant(expr[i]) : expr[i];
                            if (docChar != spanChar) {
                                return false;
                            }
                            break;
                        }
                }
            }
            return true;
        }

        bool MarkTokensInLine(IDocument document, int lineNumber, ref bool spanChanged)
        {
            currentLineNumber = lineNumber;
            bool processNextLine = false;
            LineSegment previousLine = (lineNumber > 0 ? document.GetLineSegment(lineNumber - 1) : null);

            currentSpanStack = ((previousLine != null && previousLine.HighlightSpanStack != null) ? previousLine.HighlightSpanStack.Clone() : null);
            if (currentSpanStack != null) {
                while (!currentSpanStack.IsEmpty && currentSpanStack.Peek().StopEOL) {
                    currentSpanStack.Pop();
                }
                if (currentSpanStack.IsEmpty) {
                    currentSpanStack = null;
                }
            }

            currentLine = (LineSegment)document.LineSegmentCollection[lineNumber];

            if (currentLine.Length == -1) { // happens when buffer is empty !
                return false;
            }

            List<TextWord> words = ParseLine(document);

            if (currentSpanStack != null && currentSpanStack.IsEmpty) {
                currentSpanStack = null;
            }

            // Check if the span state has changed, if so we must re-render the next line
            // This check may seem utterly complicated but I didn't want to introduce any function calls
            // or allocations here for perf reasons.
            if(currentLine.HighlightSpanStack != currentSpanStack) {
                if (currentLine.HighlightSpanStack == null) {
                    processNextLine = false;
                    foreach (Span sp in currentSpanStack) {
                        if (!sp.StopEOL) {
                            spanChanged = true;
                            processNextLine = true;
                            break;
                        }
                    }
                } else if (currentSpanStack == null) {
                    processNextLine = false;
                    foreach (Span sp in currentLine.HighlightSpanStack) {
                        if (!sp.StopEOL) {
                            spanChanged = true;
                            processNextLine = true;
                            break;
                        }
                    }
                } else {
                    SpanStack.Enumerator e1 = currentSpanStack.GetEnumerator();
                    SpanStack.Enumerator e2 = currentLine.HighlightSpanStack.GetEnumerator();
                    bool done = false;
                    while (!done) {
                        bool blockSpanIn1 = false;
                        while (e1.MoveNext()) {
                            if (!((Span)e1.Current).StopEOL) {
                                blockSpanIn1 = true;
                                break;
                            }
                        }
                        bool blockSpanIn2 = false;
                        while (e2.MoveNext()) {
                            if (!((Span)e2.Current).StopEOL) {
                                blockSpanIn2 = true;
                                break;
                            }
                        }
                        if (blockSpanIn1 || blockSpanIn2) {
                            if (blockSpanIn1 && blockSpanIn2) {
                                if (e1.Current != e2.Current) {
                                    done = true;
                                    processNextLine = true;
                                    spanChanged = true;
                                }
                            } else {
                                spanChanged = true;
                                done = true;
                                processNextLine = true;
                            }
                        } else {
                            done = true;
                            processNextLine = false;
                        }
                    }
                }
            } else {
                processNextLine = false;
            }

            //// Alex: remove old words
            if (currentLine.Words!=null) currentLine.Words.Clear();
            currentLine.Words = words;
            currentLine.HighlightSpanStack = (currentSpanStack != null && !currentSpanStack.IsEmpty) ? currentSpanStack : null;

            return processNextLine;
        }

        List<TextWord> ParseLine(IDocument document)
        {
            List<TextWord> words = new List<TextWord>();
            HighlightColor markNext = null;

            currentOffset = 0;
            currentLength = 0;
            UpdateSpanStateVariables();

            int currentLineLength = currentLine.Length;
            int currentLineOffset = currentLine.Offset;

            for (int i = 0; i < currentLineLength; ++i) {
                char ch = document.GetCharAt(currentLineOffset + i);
                switch (ch) {
                    case '\n':
                    case '\r':
                        PushCurWord(document, ref markNext, words);
                        ++currentOffset;
                        break;
                    case ' ':
                        PushCurWord(document, ref markNext, words);
                        if (activeSpan != null && activeSpan.Color.HasBackground) {
                            words.Add(new TextWord.SpaceTextWord(activeSpan.Color));
                        } else {
                            words.Add(TextWord.Space);
                        }
                        ++currentOffset;
                        break;
                    case '\t':
                        PushCurWord(document, ref markNext, words);
                        if (activeSpan != null && activeSpan.Color.HasBackground) {
                            words.Add(new TextWord.TabTextWord(activeSpan.Color));
                        } else {
                            words.Add(TextWord.Tab);
                        }
                        ++currentOffset;
                        break;
                    default:
                        {
                            // handle escape characters
                            char escapeCharacter = '\0';
                            if (activeSpan != null && activeSpan.EscapeCharacter != '\0') {
                                escapeCharacter = activeSpan.EscapeCharacter;
                            } else if (activeRuleSet != null) {
                                escapeCharacter = activeRuleSet.EscapeCharacter;
                            }
                            if (escapeCharacter != '\0' && escapeCharacter == ch) {
                                // we found the escape character
                                if (activeSpan != null && activeSpan.End != null && activeSpan.End.Length == 1
                                    && escapeCharacter == activeSpan.End[0])
                                {
                                    // the escape character is a end-doubling escape character
                                    // it may count as escape only when the next character is the escape, too
                                    if (i + 1 < currentLineLength) {
                                        if (document.GetCharAt(currentLineOffset + i + 1) == escapeCharacter) {
                                            currentLength += 2;
                                            PushCurWord(document, ref markNext, words);
                                            ++i;
                                            continue;
                                        }
                                    }
                                } else {
                                    // this is a normal \-style escape
                                    ++currentLength;
                                    if (i + 1 < currentLineLength) {
                                        ++currentLength;
                                    }
                                    PushCurWord(document, ref markNext, words);
                                    ++i;
                                    continue;
                                }
                            }

                            // highlight digits
                            if (!inSpan && (Char.IsDigit(ch) || (ch == '.' && i + 1 < currentLineLength && Char.IsDigit(document.GetCharAt(currentLineOffset + i + 1)))) && currentLength == 0) {
                                bool ishex = false;
                                bool isfloatingpoint = false;

                                if (ch == '0' && i + 1 < currentLineLength && Char.ToUpper(document.GetCharAt(currentLineOffset + i + 1)) == 'X') { // hex digits
                                    const string hex = "0123456789ABCDEF";
                                    ++currentLength;
                                    ++i; // skip 'x'
                                    ++currentLength;
                                    ishex = true;
                                    while (i + 1 < currentLineLength && hex.IndexOf(Char.ToUpper(document.GetCharAt(currentLineOffset + i + 1))) != -1) {
                                        ++i;
                                        ++currentLength;
                                    }
                                } else {
                                    ++currentLength;
                                    while (i + 1 < currentLineLength && Char.IsDigit(document.GetCharAt(currentLineOffset + i + 1))) {
                                        ++i;
                                        ++currentLength;
                                    }
                                }
                                if (!ishex && i + 1 < currentLineLength && document.GetCharAt(currentLineOffset + i + 1) == '.') {
                                    isfloatingpoint = true;
                                    ++i;
                                    ++currentLength;
                                    while (i + 1 < currentLineLength && Char.IsDigit(document.GetCharAt(currentLineOffset + i + 1))) {
                                        ++i;
                                        ++currentLength;
                                    }
                                }

                                if (i + 1 < currentLineLength && Char.ToUpper(document.GetCharAt(currentLineOffset + i + 1)) == 'E') {
                                    isfloatingpoint = true;
                                    ++i;
                                    ++currentLength;
                                    if (i + 1 < currentLineLength && (document.GetCharAt(currentLineOffset + i + 1) == '+' || document.GetCharAt(currentLine.Offset + i + 1) == '-')) {
                                        ++i;
                                        ++currentLength;
                                    }
                                    while (i + 1 < currentLine.Length && Char.IsDigit(document.GetCharAt(currentLineOffset + i + 1))) {
                                        ++i;
                                        ++currentLength;
                                    }
                                }

                                if (i + 1 < currentLine.Length) {
                                    char nextch = Char.ToUpper(document.GetCharAt(currentLineOffset + i + 1));
                                    if (nextch == 'F' || nextch == 'M' || nextch == 'D') {
                                        isfloatingpoint = true;
                                        ++i;
                                        ++currentLength;
                                    }
                                }

                                if (!isfloatingpoint) {
                                    bool isunsigned = false;
                                    if (i + 1 < currentLineLength && Char.ToUpper(document.GetCharAt(currentLineOffset + i + 1)) == 'U') {
                                        ++i;
                                        ++currentLength;
                                        isunsigned = true;
                                    }
                                    if (i + 1 < currentLineLength && Char.ToUpper(document.GetCharAt(currentLineOffset + i + 1)) == 'L') {
                                        ++i;
                                        ++currentLength;
                                        if (!isunsigned && i + 1 < currentLineLength && Char.ToUpper(document.GetCharAt(currentLineOffset + i + 1)) == 'U') {
                                            ++i;
                                            ++currentLength;
                                        }
                                    }
                                }

                                words.Add(new TextWord(document, currentLine, currentOffset, currentLength, DigitColor, false));
                                currentOffset += currentLength;
                                currentLength = 0;
                                continue;
                            }

                            // Check for SPAN ENDs
                            if (inSpan) {
                                if (activeSpan.End != null && activeSpan.End.Length > 0) {
                                    if (MatchExpr(currentLine, activeSpan.End, i, document, activeSpan.IgnoreCase)) {
                                        PushCurWord(document, ref markNext, words);
                                        string regex = GetRegString(currentLine, activeSpan.End, i, document);
                                        currentLength += regex.Length;
                                        words.Add(new TextWord(document, currentLine, currentOffset, currentLength, activeSpan.EndColor, false));
                                        currentOffset += currentLength;
                                        currentLength = 0;
                                        i += regex.Length - 1;
                                        currentSpanStack.Pop();
                                        UpdateSpanStateVariables();
                                        continue;
                                    }
                                }
                            }

                            // check for SPAN BEGIN
                            if (activeRuleSet != null) {
                                foreach (Span span in activeRuleSet.Spans) {
                                    if ((!span.IsBeginSingleWord || currentLength == 0)
                                        && (!span.IsBeginStartOfLine.HasValue || span.IsBeginStartOfLine.Value == (currentLength == 0 && words.TrueForAll(delegate(TextWord textWord) { return textWord.Type != TextWordType.Word; })))
                                        && MatchExpr(currentLine, span.Begin, i, document, activeRuleSet.IgnoreCase)) {
                                        PushCurWord(document, ref markNext, words);
                                        string regex = GetRegString(currentLine, span.Begin, i, document);

                                        if (!OverrideSpan(regex, document, words, span, ref i)) {
                                            currentLength += regex.Length;
                                            words.Add(new TextWord(document, currentLine, currentOffset, currentLength, span.BeginColor, false));
                                            currentOffset += currentLength;
                                            currentLength = 0;

                                            i += regex.Length - 1;
                                            if (currentSpanStack == null) {
                                                currentSpanStack = new SpanStack();
                                            }
                                            currentSpanStack.Push(span);
                                            span.IgnoreCase = activeRuleSet.IgnoreCase;

                                            UpdateSpanStateVariables();
                                        }

                                        goto skip;
                                    }
                                }
                            }

                            // check if the char is a delimiter
                            if (activeRuleSet != null && (int)ch < 256 && activeRuleSet.Delimiters[(int)ch]) {
                                PushCurWord(document, ref markNext, words);
                                if (currentOffset + currentLength +1 < currentLine.Length) {
                                    ++currentLength;
                                    PushCurWord(document, ref markNext, words);
                                    goto skip;
                                }
                            }

                            ++currentLength;
                            skip: continue;
                        }
                }
            }

            PushCurWord(document, ref markNext, words);

            OnParsedLine(document, currentLine, words);

            return words;
        }

        /// <summary>
        /// pushes the curWord string on the word list, with the
        /// correct color.
        /// </summary>
        void PushCurWord(IDocument document, ref HighlightColor markNext, List<TextWord> words)
        {
            // Svante Lidman : Need to look through the next prev logic.
            if (currentLength > 0) {
                if (words.Count > 0 && activeRuleSet != null) {
                    TextWord prevWord = null;
                    int pInd = words.Count - 1;
                    while (pInd >= 0) {
                        if (!((TextWord)words[pInd]).IsWhiteSpace) {
                            prevWord = (TextWord)words[pInd];
                            if (prevWord.HasDefaultColor) {
                                PrevMarker marker = (PrevMarker)activeRuleSet.PrevMarkers[document, currentLine, currentOffset, currentLength];
                                if (marker != null) {
                                    prevWord.SyntaxColor = marker.Color;
            //									document.Caret.ValidateCaretPos();
            //									document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.SingleLine, document.GetLineNumberForOffset(document.Caret.Offset)));
                                }
                            }
                            break;
                        }
                        pInd--;
                    }
                }

                if (inSpan) {
                    HighlightColor c = null;
                    bool hasDefaultColor = true;
                    if (activeSpan.Rule == null) {
                        c = activeSpan.Color;
                    } else {
                        c = GetColor(activeRuleSet, document, currentLine, currentOffset, currentLength);
                        hasDefaultColor = false;
                    }

                    if (c == null) {
                        c = activeSpan.Color;
                        if (c.Color == Color.Transparent) {
                            c = this.DefaultTextColor;
                        }
                        hasDefaultColor = true;
                    }
                    words.Add(new TextWord(document, currentLine, currentOffset, currentLength, markNext != null ? markNext : c, hasDefaultColor));
                } else {
                    HighlightColor c = markNext != null ? markNext : GetColor(activeRuleSet, document, currentLine, currentOffset, currentLength);
                    if (c == null) {
                        words.Add(new TextWord(document, currentLine, currentOffset, currentLength, this.DefaultTextColor, true));
                    } else {
                        words.Add(new TextWord(document, currentLine, currentOffset, currentLength, c, false));
                    }
                }

                if (activeRuleSet != null) {
                    NextMarker nextMarker = (NextMarker)activeRuleSet.NextMarkers[document, currentLine, currentOffset, currentLength];
                    if (nextMarker != null) {
                        if (nextMarker.MarkMarker && words.Count > 0) {
                            TextWord prevword = ((TextWord)words[words.Count - 1]);
                            prevword.SyntaxColor = nextMarker.Color;
                        }
                        markNext = nextMarker.Color;
                    } else {
                        markNext = null;
                    }
                }
                currentOffset += currentLength;
                currentLength = 0;
            }
        }

        void ResolveExternalReferences()
        {
            foreach (HighlightRuleSet ruleSet in Rules) {
                ruleSet.Highlighter = this;
                if (ruleSet.Reference != null) {
                    IHighlightingStrategy highlighter = HighlightingManager.Manager.FindHighlighter (ruleSet.Reference);

                    if (highlighter == null)
                        throw new HighlightingDefinitionInvalidException("The mode defintion " + ruleSet.Reference + " which is refered from the " + this.Name + " mode definition could not be found");
                    if (highlighter is IHighlightingStrategyUsingRuleSets)
                        ruleSet.Highlighter = (IHighlightingStrategyUsingRuleSets)highlighter;
                    else
                        throw new HighlightingDefinitionInvalidException("The mode defintion " + ruleSet.Reference + " which is refered from the " + this.Name + " mode definition does not implement IHighlightingStrategyUsingRuleSets");
                }
            }
        }

        void ResolveRuleSetReferences()
        {
            foreach (HighlightRuleSet ruleSet in Rules) {
                if (ruleSet.Name == null) {
                    defaultRuleSet = ruleSet;
                }

                foreach (Span aSpan in ruleSet.Spans) {
                    if (aSpan.Rule != null) {
                        bool found = false;
                        foreach (HighlightRuleSet refSet in Rules) {
                            if (refSet.Name == aSpan.Rule) {
                                found = true;
                                aSpan.RuleSet = refSet;
                                break;
                            }
                        }
                        if (!found) {
                            aSpan.RuleSet = null;
                            throw new HighlightingDefinitionInvalidException("The RuleSet " + aSpan.Rule + " could not be found in mode definition " + this.Name);
                        }
                    } else {
                        aSpan.RuleSet = null;
                    }
                }
            }

            if (defaultRuleSet == null) {
                throw new HighlightingDefinitionInvalidException("No default RuleSet is defined for mode definition " + this.Name);
            }
        }

        void UpdateSpanStateVariables()
        {
            inSpan = (currentSpanStack != null && !currentSpanStack.IsEmpty);
            activeSpan = inSpan ? currentSpanStack.Peek() : null;
            activeRuleSet = GetRuleSet(activeSpan);
        }

        #endregion Methods
    }
}